// Package token: 词法单元定义包。这个包定义了词法单元类型、词法单元struct、关键字映射等内容
package token

// 词法类型常量定义
const (
	ILLEGAL = "ILLEGAL" // 表示非法地、未知地、未定义地词法类型
	EOF     = "EOF"     // End Of File 表示文件结尾

	// 标识符 + 字面量
	IDENT  = "IDENT"  // 变量名、函数名
	INT    = "INT"    // int 表示数字类型
	STRING = "STRING" // String 表示字符串类型

	// 算术运算符
	ASSIGN   = "="  // 赋值符号 一个等于
	PLUS     = "+"  // 加号
	MINUS    = "-"  // 减号
	BANG     = "!"  // 取反
	ASTERISK = "*"  // 乘法
	SLASH    = "/"  // 除法
	LT       = "<"  // 小于
	GT       = ">"  // 大于
	EQ       = "==" // 等于
	NOT_EQ   = "!=" // 不等于

	// 位运算
	AND     = "&"   // 与运算 - 有0出0，全1出1 ( 1 & 1 )
	OR      = "|"   // 或运算 - 有1出1，全0出0 ( 1 | 1 )
	NOT     = "~"   // 非运算 - 有1出0，有0出1 ( ~1 )
	XOR     = "^"   // 异或运算 - 相异为1，相同为0 (1 ^ 1)
	LSHIFT  = "<<"  // 左移运算 ( 2 << 1 ==> 2 左移 1 位，右值 必须大于 0 )
	RSHIFT  = ">>"  // 右移运算 ( 2 >> 1 ==> 2 右移 1 位. 右值 必须大于 0 )
	URSHIFT = ">>>" // 无符号右移运算 ( 1 >>> 1 ==> 2 无符号右移 1 位. 右值 必须大于 0 )

	// 逻辑运算符
	LOGIC_AND = "&&" // 逻辑与 - 两个条件都为真，则返回true，否则返回false
	LOGIC_OR  = "||" // 逻辑或 - 只要一个条件为真，则返回true，否则返回false

	// 分隔符
	COMMA     = "," // 逗号 表示参数或变量分隔
	SEMICOLON = ";" // 分号，表示语句结束
	LPAREN    = "(" // 左小括号 表示计算表达式或方法参数开始或者元组开始
	RPAREN    = ")" // 右小括号 表示计算表达式或方法参数结束或者元组结束
	LBRACE    = "{" // 左花括号 表示语句块开始
	RBRACE    = "}" // 右花括号 表示语句块结束
	LBRACKET  = "[" // 左中括号 表示数组开始
	RBRACKET  = "]" // 右中括号 表示数组结束
	COLON     = ":" // 冒号 表示键值对结构分隔

	// 关键字
	FUNCTION = "FUNCTION" // 表示函数
	LET      = "LET"      // 表示变量声明
	RETURN   = "RETURN"   // 表示方法返回
	TRUE     = "TRUE"     // true
	FALSE    = "FALSE"    // false
	IF       = "IF"       // if
	ELSE     = "ELSE"     // else
)

// TokenType 词法单元类型: 这里使用了string作为词法类型的类型，虽然在性能上会有损失但是方便使用和调试，而且这个项目是自己练手的小项目无所谓那些了 TODO 这是一个可以改进的点
type TokenType string

// Token 词法单元: 词法分析器解析代码，进行词法分析，输出词法单元
type Token struct {
	Type    TokenType // 词法单元类型
	Literal string    // 词法单元字面量
	LineNum uint64    // 词法单元所在行的行号
}

// keywords 关键字映射Map
var keywords = map[string]TokenType{
	"fn":     FUNCTION,
	"let":    LET,
	"return": RETURN,
	"true":   TRUE,
	"false":  FALSE,
	"if":     IF,
	"else":   ELSE,
}

// LookupIdent 获取标识符映射的词法单元类型，如果关键字映射表中能找到对应的词法单元类型，那么可以认为这是一个关键字
//
//	否则认为是开发者自定义的标识符(即变量名、函数名...)
func LookupIdent(ident string) TokenType {
	if tokenType, ok := keywords[ident]; ok {
		return tokenType
	}
	return IDENT
}
